Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iteratorToArray Support #2894

Closed
wants to merge 1 commit into from
Closed

Conversation

matinFT
Copy link

@matinFT matinFT commented Dec 17, 2020

No description provided.

Copy link
Collaborator

@jgonggrijp jgonggrijp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @matinFT for taking the effort to submit a PR!

Unfortunately, I have to reject this particular PR for reasons I'll explain below. However, I'd like to encourage you to submit PRs again in the future. For this reason, I've decided to review your changes in detail, so you can already learn about some of the things that you need to take into account when contributing to Underscore. You'll find my comments after this post.

The reason that I'm rejecting this PR, is that Underscore needs to be consistent. If we're going to support iterables, then we should support them throughout the library rather than in just one function; in fact, iterables should become one of the main collection types next to array, object, map and set. This means that adding iterable support is a big breaking change. However, we recognize that it is very desirable to have, so it is planned for Underscore version 2.0. I dedicated #2147 as the central ticket for this plan. I previously mentioned the need for wholesale iterable support in #2448 (comment).

A similar PR that was rejected with similar reasoning is #2808.

@@ -0,0 +1,7 @@
function isIterable(obj) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you intended isIterable to be a public function (i.e., mixed into the _ object), then you should list it in modules/index.js. There should also be a comment before the function to explain its purpose. Take a look at a few other public modules to get a taste of how we write these comments.

If you intended isIterable to be only an internal function instead, then you should start the name of the module with a leading underscore, i.e., modules/_isIterable.js. In this case it would still be desirable to have a comment that explains the purpose of the function, but I'll readily admit that there are currently more internal functions where this is missing.

@@ -0,0 +1,7 @@
function isIterable(obj) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, you forgot export default.

@@ -0,0 +1,7 @@
function isIterable(obj) {
// checks for null and undefined
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an unnecessary comment.

if (obj == null) {
return false;
}
return typeof obj[Symbol.iterator] === 'function';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't safely assume that Symbol or Symbol.iterator is defined, because that will (needlessly!) break compatibility with older environments that only know ES3. Instead, we would use feature detection and then create an internal getIterator function that simply returns undefined in environments where these objects don't exist. You can see an example of this practice in modules/keys.js, which uses Object.keys but only if it exists.

A similar, but more subtle, consideration applies to typeof x === 'function'. In some environments, this expression doesn't work as intended. This is why you should use _.isFunction instead whenever possible. isFunction(x) can also be minified much more compactly than typeof x === 'function'.

So this line should have read like this:

Suggested change
return typeof obj[Symbol.iterator] === 'function';
return isFunction(getIterator(obj));

Comment on lines +3 to +6
if (obj == null) {
return false;
}
return typeof obj[Symbol.iterator] === 'function';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given my previous comment, the entire body of this function would actually fit into a single expression:

Suggested change
if (obj == null) {
return false;
}
return typeof obj[Symbol.iterator] === 'function';
return obj != null && isFunction(getIterator(obj));

Though getIterator should probably check against null and undefined anyway, so you could actually leave out the null check entirely.

@@ -0,0 +1,9 @@
export default function iteratorToArray(iterator) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like with isIterable, you should either add a descriptive comment and list this function in modules/index.js if you intended this as a public function, or start the module name with _ otherwise. However, since this function seems to be purely an implementation detail of _.toArray, it should probably be internal, and maybe it should even be defined inside modules/toArray.js.

@@ -5,7 +5,8 @@ import isArrayLike from './_isArrayLike.js';
import map from './map.js';
import identity from './identity.js';
import values from './values.js';

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This blank line should stay.

@@ -16,5 +17,6 @@ export default function toArray(obj) {
return obj.match(reStrSymbol);
}
if (isArrayLike(obj)) return map(obj, identity);
else if (isIterable(obj)) return iteratorToArray(obj[Symbol.iterator])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When adding a feature to Underscore, you always also have to add tests to verify that the feature works as intended. This is especially important because this enables us to notice when a later change breaks a pre-existing feature.

I'm mentioning this here because _.toArray is already a public function. If isIterable or iteratorToArray was public as well, though, you'd have to add tests for that, too.

@jgonggrijp jgonggrijp closed this Dec 17, 2020
@jgonggrijp jgonggrijp linked an issue Dec 17, 2020 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

iterator support for _.toArray
2 participants